#include "sphere.hpp"

Sphere::Sphere( const vec3f &c, f32 r, const vec3f &col )
    : center( c ), radius( r ), color( col ) {}

bool Sphere::DoCalculation( const Ray &r, f32 tmin, f32 tmax, HitRecord &record,
                            bool isShadowHit ) const {
  vec3f tmp = r.origin() - center;
  f64 a = dot( r.direction(), r.direction() );
  f64 b = 2 * dot( r.direction(), tmp );
  f64 c = dot( tmp, tmp ) - radius * radius;

  f64 discriminant = b * b - 4 * a * c;
  if ( discriminant > 0 ) {
    discriminant = std::sqrt( discriminant );
    f64 t = ( -b - discriminant ) / ( 2 * a );

    if ( t < tmin ) {
      t = ( -b + discriminant ) / 2 * a;
    }
    if ( t < tmin || t > tmax ) {
      return false;
    }

    if ( !isShadowHit ) {
      record.t = static_cast<f32>(t);
      record.normal = normalize( ( r(static_cast<f32>(t)) - center ) );
      record.color = color;
    }
    return true;
  }
  return false;
}

bool Sphere::InSphere( const Ray &r, f32 tmin, f32 tmax ) const {
  // use binary sort
  // TODO: this method may have performance issue
  // one case : when sphere is very small, need do more binary divide
  return false;
}

bool Sphere::hit( const Ray &r, f32 tmin, f32 tmax, f32 time,
                  HitRecord &record ) {
  return DoCalculation( r, tmin, tmax, record, false );
}

bool Sphere::shadowHit( const Ray &r, f32 tmin, f32 tmax, f32 time ) {
  HitRecord record;
  // TODO: optimize here
  return DoCalculation( r, tmin, tmax, record, true );
}